import { IWorkerController, WorkerMessageType, IWorkerMessage } from './worker.core';

const COMMUNICATION_TIMEOUT = 30000;

/**
 * 通信通道
 */
export class WorkerChannel {
    /**
     * Web Worker 实例
     */
    private worker: Worker;

    /**
     * 上层通信控制器
     */
    private controller: IWorkerController;
    
    /**
     * 会话响应器 Map
     */
    private sessionHandlerMap: {
        [propsName: string]: Function;
    };

    public constructor(worker: Worker, controller: IWorkerController) {
        this.worker = worker;
        this.controller = controller;

        this.sessionHandlerMap = {};

        // 绑定 worker onmessage 事件的回调
        this.worker.addEventListener('message', this.onmessage.bind(this));
    }

    /**
     * 发送响应
     *
     * @param sessionId 会话 Id
     * @param payload 负载
     */
    public response(sessionId: string, actionType: string, payload: any): void {
        this.postMessage({
            messageType: WorkerMessageType.REPLY,
            actionType,
            payload,
            sessionId,
        });
    }

    /**
     * 发送请求, 不等待响应
     *
     * @param actionType 事务类型
     * @param payload 负载
     */
    public request(actionType: string, payload: any): void {
        const sessionId = this.generateSessionId();
        this.postMessage({
            messageType: WorkerMessageType.REQUEST,
            actionType,
            payload,
            sessionId,
        });

        // 不等待结果, 还会收到响应, 添加个空的会话响应器
        this.addSessionHandler(sessionId, () => {});
    }

    /**
     * 发送请求, 并等待响应
     *
     * @param actionType 事务类型
     * @param payload 负载
     * @param timeout 响应超时
     * @returns {Promise<IWorkerMessage>} 等待响应的 Promise
     */
    public requestPromise<T>(actionType: string, payload: any, timeout = COMMUNICATION_TIMEOUT): Promise<T> {
        const sessionId = this.generateSessionId();
        const message = {
            messageType: WorkerMessageType.REQUEST,
            actionType,
            payload,
            sessionId,
        };

        // 请求封装为一个 Promise, 等待会话响应器进行 resolve
        const PromiseFunction = (resolve: Function, reject: Function): any => {
            // 启动请求超时计时器
            const timeoutHandler = setTimeout(() => {
                clearTimeout(timeoutHandler);

                reject();
            }, timeout);

            const sessionHandler: Function = (message: IWorkerMessage) => {
                // 会话回调函数, 开始处理响应
                this.deleteSessionHandler(message.sessionId);
                clearTimeout(timeoutHandler);

                resolve(message.payload);
            };

            this.addSessionHandler(sessionId, sessionHandler);

            // 开始发送请求
            this.postMessage(message);
        };

        return new Promise(PromiseFunction);
    }

    /**
     * 收到会话消息的处理函数
     *
     * 发现是请求, 调用通信控制器的事务处理器进行处理, 获取事务结果并响应;
     * 发现是响应，调用会话响应器
     * @param event worker 通信事件
     */
    private onmessage(event: { data: IWorkerMessage }): void {
        const { data: message } = event;
        const { messageType, sessionId, actionType } = message;

        // 接收到请求
        if (messageType === WorkerMessageType.REQUEST) {
            // 处理请求
            this.controller.actionHandler(message)
                .then(actionResult => {
                    // 响应请求
                    this.response(sessionId, actionType, actionResult);
                });
        }

        // 接收到响应
        if (messageType === WorkerMessageType.REPLY) {
            // 处理响应
            if (this.hasSessionHandler(sessionId)) {
                this.sessionHandlerMap[sessionId](message);
            } else {
                throw new Error(`Session \`${sessionId}\` handler no exist`);
            }
        }
    }

    /**
     * 封装的 worker 原生 postMessage 接口
     * 支持 structured clone 和 transfer 2种通信模式
     *
     * @param message 会话消息
     */
    private postMessage(message: IWorkerMessage): void {
        this.worker.postMessage(message);
    }

    /**
     * 添加会话响应器
     *
     * @param sessionId 会话 Id
     * @param handler 会话响应器
     */
    private addSessionHandler(sessionId: string, handler: Function): void {
        if (!this.hasSessionHandler(sessionId)) {
            this.sessionHandlerMap[sessionId] = handler;
        } else {
            throw new Error(`SessionId \`${sessionId}\` already exist!`);
        }
    }

    /**
     * 移除会话响应器
     *
     * @param sessionId
     */
    private deleteSessionHandler(sessionId: string): void {
        if (this.hasSessionHandler(sessionId)) {
            delete this.sessionHandlerMap[sessionId];
        }
    }

    /**
     * 生成每次独立会话的 Id
     *
     * @returns 会话 Id
     */
    private generateSessionId(): string {
        const sessionId = `w_${BI.UUID()}`;

        return sessionId;
    }

    /**
     * 判断是否有指定会话的处理器
     *
     * @param sessionId 会话 Id
     * @returns {boolean} 判断结果
     */
    private hasSessionHandler(sessionId: string): boolean {
        return !!this.sessionHandlerMap[sessionId];
    }
}
